#include "sim900.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "app.h"
#include "..\..\APP\includes.h"
#include "ringbuffer.h"
#include "cJSON.h"
#include "malloc.h"
#include "GE_COMMON2.h"
#include "flexcan.h"
#include "m25p16.h"

uint8_t connect_cmd[64] = "AT+CIPSTART=\"TCP\",\"139.196.46.68\",\"9999\"\r\n";
//uint8_t connect_cmd[64] = "AT+CIPSTART=\"TCP\",\"117.81.228.156\",\"9999\"\r\n";
static uint8_t rx_buf[100];
static struct rb rx_rb;
OS_EVENT *rx_semaphore;

#define RECEIVE_TASK_STK_SIZE       1024
#define SIM900_RX_PRIO              5
OS_STK recvTaskStk[RECEIVE_TASK_STK_SIZE];

//heartbeat count
#define HEARTBEAT_TASK_STK_SIZE     256
#define SIM900_HEARTBEAT_PRIO              6
OS_STK heartbeatTaskStk[HEARTBEAT_TASK_STK_SIZE];

#define BUF_LEN		128

__IO char rx_buffer[BUF_LEN];
__IO int rx_offset;

ControlCmd controlTable[];
extern DataType vehicle_data[];

static void thread_sim900_rx_entry(void *parg);
static void thread_heartbeat_entry(void *parg);
static uint8_t heartbeat_count = 0;
static uint8_t heartbeat_no_rsp = 0;

static uint8_t status = STATUS_POWERON;
static uint8_t heartbeat_is_running = 0;

static uint8_t is_busy = 0;
__align(8) static uint8_t device_id[17];
extern Result result[20];

void clear_rx_buf(void)
{
	rx_offset = 0;
	memset((void *)rx_buffer, 0x00, BUF_LEN);
}

void sim900_poweron(void)
{

    GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 

	if(is_busy) {
		printk("%s: is busy!\r\n", __func__);
		return;
	}

	is_busy = 1;
	printk("sim900 reset...\r\n");
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);					 

	GPIO_ResetBits(GPIOB, GPIO_Pin_0);
	OSTimeDlyHMSM(0, 0, 4, 0);
	GPIO_SetBits(GPIOB, GPIO_Pin_0);
	OSTimeDlyHMSM(0, 0, 4, 0);
	GPIO_ResetBits(GPIOB, GPIO_Pin_0);

    is_busy = 0;
}

void sim900_init(void)
{
    u8 data;
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    if (SIM900_USARTAPB == APB1)
    {
        RCC_APB2PeriphClockCmd(RCC_APBxPeriph_SIM900_USART_IO | RCC_APB2Periph_AFIO,ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APBxPeriph_SIM900_USART,ENABLE);
    }
    else
    {
        RCC_APB2PeriphClockCmd(RCC_APBxPeriph_SIM900_USART_IO | RCC_APBxPeriph_SIM900_USART | RCC_APB2Periph_AFIO,ENABLE);
    }
    if (SIM900_PinRemap == ENABLE)
    {  				 
        GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
    }
    GPIO_InitStructure.GPIO_Pin = SIM900_USART_TXD;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SIM900_USART_IO,&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = SIM900_USART_RXD;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SIM900_USART_IO,&GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;
    USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    USART_Init(SIM900_USART,&USART_InitStructure);
    data = data;
    data = SIM900_USART->DR;
    data = SIM900_USART->SR;
    USART_ITConfig(SIM900_USART,USART_IT_RXNE,ENABLE);
    USART_Cmd(SIM900_USART,ENABLE);

	rb_init(&rx_rb, &rx_buf[0], sizeof(rx_buf));
	rx_semaphore = OSSemCreate(0);

    //setup recieve thread
    OSTaskCreate(thread_sim900_rx_entry, (void *)0, 
            &recvTaskStk[RECEIVE_TASK_STK_SIZE - 1],
            SIM900_RX_PRIO);
	status = STATUS_POWERON;
    //get device id
    sim900_get_device_id();
}

void send(uint8_t c)
{
    USART_SendData(SIM900_USART, c);
    while(USART_GetFlagStatus(SIM900_USART, USART_FLAG_TXE) == RESET)
    {;}
}

void sim900_write(uint8_t *buf, uint16_t size)
{
    //INT8U   err;
    uint16_t i = 0;
    //SIM900_LOCK();
    for(; i < size; i++) {
        send(buf[i]);
    }
    //SIM900_UNLOCK();
}

void at(void)
{
	sim900_write("AT\r\n", 4);
}

void clear_buf(void)
{
	uint8_t i;
	for(i = 0; i < BUF_LEN; i++) {
		rx_buffer[i] = 0x00;
	}
	rx_offset = 0;
}

int check_at(void)
{
	char *tmp = NULL;
	
	tmp = strstr((char *)rx_buffer, "OK");	
	if(tmp)
		return 0;
	else
		return -1;
}

int sim900_get_device_id(void)
{
#if 0
    uint8_t i;
	int ret = 0;
	uint8_t tmp;
    const uint8_t cmd_offset = 10;
    for(i = 0; i < 14; i++) {
		tmp = rx_buffer[i+cmd_offset] & 0xff;
		device_id[i] = tmp;
		printk("[%d] = %c", i, tmp);
		if((tmp < (uint8_t)'0') || (tmp > (uint8_t)'9')) {
			printk("failed\r\n");
			ret = -1;
		}
    }
    device_id[14] = '0';
    device_id[15] = '1';
    device_id[16] = '\0';
    printk("\r\ndevice id: %s\r\n\r\n", device_id);

	return ret;
#endif
    flash_page_read(device_id, DEVICE_ID_ADDRESS, 17);
    printk("\r\ndevice id: %s\r\n\r\n", device_id);
}

int check_connect(void)
{
	char *tmp = NULL;
	
	printk("buf = %s\r\n", rx_buffer);
	tmp = strstr((char *)rx_buffer, "CONNECT");	
	if(tmp)
		return 0;
	else
		return -1;
}

void sim900_wait(int ticks)
{
	int wait = ticks;
	while(--wait > 0)
		;
}

void sim900_connect(void)
{
	uint8_t i = 0;
	uint8_t retry = 3;

	printk("connect...\r\n");

	while(1) {
		switch(status) {
			case STATUS_POWERON:
				sim900_poweron();
				//sim900_init();
				//sim900_write("AT\r\n", 4);
				clear_rx_buf();
				
				printk("wait for at done\r\n");
				for(i = 0; i < retry; i++) {
                    printk("\r\n[SOME]\r\n");
				    sim900_write("AT\r\n", 4);
					//sim900_wait(5000000);
					OSTimeDlyHMSM(0, 0, 0, 400);
					if(!check_at()) {
						status = STATUS_CONFIG;
						break;
					}
					clear_rx_buf();
				}
				break;
				
			//case STATUS_INIT:
			case STATUS_CONFIG:
				printk("wait for config done\r\n");
				OSTimeDlyHMSM(0, 0, 20, 0);
			    sim900_write("AT+CREG?\r\n", 10);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CIPMODE=1\r\n", 14);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CGATT?\r\n", 11);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CSTT=\"CMNET\"\r\n", 17);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CIICR\r\n", 10);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CIFSR\r\n", 10);
				OSTimeDlyHMSM(0, 0, 0, 500);
			    sim900_write("AT+CGATT=1\r\n", 12);
				OSTimeDlyHMSM(0, 0, 0, 500);
                //get device_id
#if 0
                OSTimeDlyHMSM(0, 0, 2, 0);
				for(i = 0; i < 3; i++) {
	                clear_buf();
		            sim900_write("AT+CGSN\r\n", 9);
	                OSTimeDlyHMSM(0, 0, 0, 500);
					if(!sim900_get_device_id()) {
						break;
					}		
				}
#endif
				status = STATUS_CONNECTING;
				break;
			case STATUS_CONNECTING:
				printk("wait for connecting...\r\n");
                
                printk("\r\ndevice id: %s\r\n\r\n", device_id);
				sim900_write("AT+CIPSTATUS\r\n", 14);
				OSTimeDlyHMSM(0, 0, 1, 0);
				OSTimeDlyHMSM(0, 0, 2, 0);
				for(i = 0; i < 2; i++) {
                    //sim900_write("AT+CIPCLOSE\r\n", 13);
                    //OSTimeDlyHMSM(0, 0, 2, 0);
				    clear_rx_buf();
					sim900_write(connect_cmd, strlen((char *)&connect_cmd[0]));
					OSTimeDlyHMSM(0, 0, 3, 0);
					if(!check_at())  {
						status = STATUS_CONNECTED;
						break;
					}
					clear_rx_buf();
				}
				if(i >= 2) {		//retry failed
					sim900_write("+++\r\n", 5);
					OSTimeDlyHMSM(0, 0, 1, 0);
					sim900_write("AT+CIPCLOSE\r\n", 13);
					OSTimeDlyHMSM(0, 0, 1, 0);
					sim900_write("AT+CIPSHUT\r\n", 12);
					OSTimeDlyHMSM(0, 0, 1, 0);
					status = STATUS_CONFIG;
				}
				break;
			case STATUS_CONNECTED:
				printk("Connect done!\r\n");
				clear_buf();
				//setup heartbeat thread
				if(!heartbeat_is_running) {
				    OSTaskCreate(thread_heartbeat_entry, (void *)0, 
				            &heartbeatTaskStk[HEARTBEAT_TASK_STK_SIZE - 1],
				            SIM900_HEARTBEAT_PRIO);
				}
				return;
			default:
				break;
		}
	}
}

void config(void)
{
	OSTimeDlyHMSM(0, 0, 15, 0);
	printk("wait 15 seconds\r\n");
    //sim900_write("AT+CPIN?\r\n", 10);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    //sim900_write("AT+CSQ\r\n", 8);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CREG?\r\n", 10);
	OSTimeDlyHMSM(0, 0, 1, 0);

    sim900_write("AT+CIPMODE=1\r\n", 14);
	OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CGATT?\r\n", 11);
	OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CSTT=\"CMNET\"\r\n", 17);
	OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CIICR\r\n", 10);
	OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CIFSR\r\n", 10);
	OSTimeDlyHMSM(0, 0, 1, 0);
    //sim900_write("AT+CGDCONT=1,\"IP\",\"CMNET\"\r\n", 27);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write("AT+CGATT=1\r\n", 12);
	OSTimeDlyHMSM(0, 0, 1, 0);
    //sim900_write("AT+CIPCSGP=1,\"CMNET\"\r\n", 22);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    //sim900_write("AT+CIPMODE=1\r\n", 14);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    //sim900_write("AT+CLPORT=\"TCP\",\"2000\"\r\n", 24);
	//OSTimeDlyHMSM(0, 0, 1, 0);
    sim900_write(connect_cmd, strlen((char *)&connect_cmd[0]));
	OSTimeDlyHMSM(0, 0, 6, 0);
    //sim900_write("AT+CIPSTATUS\r\n", 14);
	//OSTimeDlyHMSM(0, 0, 1, 0);
	clear_buf();
	//setup heartbeat thread
    OSTaskCreate(thread_heartbeat_entry, (void *)0, 
            &heartbeatTaskStk[HEARTBEAT_TASK_STK_SIZE - 1],
            SIM900_HEARTBEAT_PRIO);
}

void sim900_setup(void)
{
	rb_init(&rx_rb, &rx_buf[0], sizeof(rx_buf));
	rx_semaphore = OSSemCreate(0);

    //setup recieve thread
    OSTaskCreate(thread_sim900_rx_entry, (void *)0, 
            &recvTaskStk[RECEIVE_TASK_STK_SIZE - 1],
            SIM900_RX_PRIO);
	at();
	config();
}

void heartbeat(void)
{
    cJSON *root = cJSON_CreateObject();
    char *out;
	uint16_t length;

	printk("do heart beat...\r\n");

	if(is_busy) {
		printk("%s: is busy!\r\n", __func__);
		return;
	}

	is_busy = 1;
	if(heartbeat_count >= 100)
		heartbeat_count = 0;
			
	cJSON_AddStringToObject(root, "deviceid", (const char *)device_id);
	cJSON_AddNumberToObject(root, "msg_type", MSG_TYPE_HEART_BEAT);
	cJSON_AddNumberToObject(root, "heartbeat_count", heartbeat_count);

	out = cJSON_Print(root);
	length = strlen(out);
    
	sim900_write((uint8_t *)out, length);
	//printf("%s\r\n", out);

    cJSON_Delete(root);
    myfree(out);

	heartbeat_count ++;
	is_busy = 0;
}

static void thread_heartbeat_entry(void *parg)
{
	parg = parg;
	heartbeat_is_running = 1;
	OSTimeDlyHMSM(0, 0, 4, 0);
	while(1) {
		//sim900_write("AT+CSQ\r\n", 8);
		printk("no rsp = %d\r\n", heartbeat_no_rsp);
		if(heartbeat_no_rsp >= 6) {
			printk("Disconnected...\r\n");
			sim900_write("+++\r\n", 5);
			OSTimeDlyHMSM(0, 0, 1, 0);
			sim900_write("AT+CIPCLOSE\r\n", 13);
			OSTimeDlyHMSM(0, 0, 1, 0);
			sim900_write("AT+CIPSHUT\r\n", 12);
			OSTimeDlyHMSM(0, 0, 1, 0);
			printk("Now restart sim900...\r\n");
			status = STATUS_CONNECTING;
			sim900_connect();
			heartbeat_no_rsp = 0;
			//return;
		}
		OSTimeDlyHMSM(0, 0, 20, 0);
		heartbeat();

		heartbeat_no_rsp ++;
	}
}
//char text[] = "{\"msg_type\":\"ctr\",\"deviceid\":\"113344\",\"bcm_fun_trunklock\":\"ON\"}";  
static void thread_sim900_rx_entry(void *parg)
{
    INT8U err;
	uint8_t start = 0;
    uint8_t thischar = 0;

    parg = parg;

    printf("Enter: %s\r\n", __func__);
    while(1) {
        OSSemPend(rx_semaphore, 0, &err);
        if(err != OS_NO_ERR)
            continue;

        while(rb_get(&rx_rb, &thischar, 1) == TRUE) {
			printf("%c", thischar);
			if(status == STATUS_CONNECTED) {
				if(thischar == '{') {
					clear_rx_buf();
					start = 1;
					rx_buffer[rx_offset ++] = thischar;
				} else if(thischar == '}') {
					rx_buffer[rx_offset ++] = thischar;
					start = 0;
					//printk("%s\r\n", rx_buffer);
                    if(rx_offset >= 10)
					    sim900_process((uint8_t *)rx_buffer);
				} else {
					if(start)
						rx_buffer[rx_offset ++] = thischar;
				}			
			} else		// init part 
			{
				rx_buffer[rx_offset ++] = thischar;
			}
        }
    }
}

void sim900_response_cmd(uint8_t cmd, uint8_t rsp)
{
    cJSON *root = cJSON_CreateObject();
    char *out;
	uint16_t length;
	
	if(is_busy) {
		printk("%s: is busy!\r\n", __func__);
		return;
	}

	is_busy = 1;
	cJSON_AddStringToObject(root, "deviceid", (const char *)device_id);
	cJSON_AddNumberToObject(root, "msg_type", MSG_TYPE_CMD_RSP);
	if(cmd == 0xff)	
		cJSON_AddStringToObject(root, "cmd_type", "unknow");
	else
		cJSON_AddStringToObject(root, "cmd_type", controlTable[cmd].key);
	cJSON_AddNumberToObject(root, "status", rsp);

	out = cJSON_Print(root);
	length = strlen(out);
    
	sim900_write((uint8_t *)out, length);
	printf("%s\r\n", out);

    cJSON_Delete(root);
    myfree(out);
	is_busy = 0;
}

void sim900_bcm(ControlCmd *cmd, bool on)
{
	uint8_t i, j;
	uint8_t count = cmd->cmd_count;

	CanTxMsg txMsg = 
	{
    	0x00, 0x18db33f1,
    	CAN_ID_STD, CAN_RTR_DATA,
    	8,
    	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};

	txMsg.StdId = cmd->id;
	for(i = 0; i < count; i++) {
		for(j = 0; j < 8; j++) {
			if(on) {
				txMsg.Data[j] = cmd->cmd_on[i][j];
			} else {
				txMsg.Data[j] = cmd->cmd_off[i][j];
			}
			//printk("->[%d] = 0x%02x\r\n", j, txMsg.Data[j]);
		}
		flexcan_send_frame2(&txMsg);
		OSTimeDlyHMSM(0, 0, 0, cmd->delay);
	}
}

void sim900_process(uint8_t *buf)
{
	cJSON *json, *item;
	char *tmp;
	uint8_t i = 0;
	int msg_type;
	uint8_t hb_count = 0;

//	if(is_busy) {
//		printk("%s: is busy!\r\n");
//		return;
//	}
//	is_busy = 1;

	json = cJSON_Parse((const char *)rx_buffer);
	if(!json) {
		printk("\r\n[%s]\r\n", cJSON_GetErrorPtr());
		sim900_response_cmd(0xff, 0xff);
		heartbeat_no_rsp = 0;
	} else {
		item = cJSON_GetObjectItem(json, "msg_type");
		msg_type = item->valueint;
		printk("msg_type = %d\r\n", msg_type);
		if(msg_type == MSG_TYPE_HEART_BEAT) {
			item = cJSON_GetObjectItem(json, "heartbeat_count");
			hb_count = item->valueint;
			printk("hb_count = %d\r\n", hb_count);
			printk("heartbeat_count = %d\r\n", heartbeat_count);
			if(((heartbeat_count - 1) * 2 + 1) == hb_count) {
				heartbeat_no_rsp = 0;
			} else {
				printk("heartbeat response error\r\n");
				//heartbeat_no_rsp = 0;
			}
		} else if(msg_type == MSG_TYPE_SEND_CMD) {
			for(i = 0; i < BCM_END; i++) {
				tmp = strstr((char *)rx_buffer, controlTable[i].key);
				if(tmp) {
					item = cJSON_GetObjectItem(json, controlTable[i].key);
					//printk("%s: = %s\r\n", controlTable[i].key, item->valuestring);
					//on
					if(!strcmp(item->valuestring, "ON")) {
						sim900_bcm(&controlTable[i], TRUE);
						sim900_response_cmd(i, 0);
					} else if(!strcmp(item->valuestring, "OFF")) {
						sim900_bcm(&controlTable[i], FALSE);
						sim900_response_cmd(i, 0);
					}
				}
			}
        } else if(msg_type == MSG_TYPE_CMD_CLEAR_CODE) {
            while(is_busy) {
		        OSTimeDlyHMSM(0, 0, 1, 0);
                printk("busy...\r\n");
            }
            is_busy = 1;
            sim900_clear_code();
            is_busy = 0;
        }

		cJSON_Delete(json);
	}			
	clear_buf();
//	is_busy = 0;
}

void sim900_clear_code(void)
{
    uint8_t i, j;
	CanTxMsg txMsg = 
	{
    	0x00, 0x18db33f1,
    	CAN_ID_STD, CAN_RTR_DATA,
    	8,
    	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};

    for(i = 0; i < CLEAR_CODE_END; i++) {
        txMsg.StdId = clearCodeTable[i].id;
        printk("clear id = %04x\r\n", txMsg.StdId);
        for(j = 0; j < 8; j++) {
            txMsg.Data[j] = clearCodeTable[i].cmd[j];
        }
		flexcan_send_frame2(&txMsg);
		OSTimeDlyHMSM(0, 0, 0, clearCodeTable[i].delay);
    }
}

void sim900_update(Result *ret, uint8_t size)
{
	uint8_t updated;
	uint8_t i, j, index, n;
	Result *res;
	uint8_t count;
    cJSON *root = cJSON_CreateObject();
    char *out = NULL;
	char val_buf[16];
	uint16_t length;
	uint8_t has_data = 0;
	
	//if disconnect, return
	if(status != STATUS_CONNECTED)
		return;

	if(is_busy) {
		printk("%s: is busy!\r\n", __func__);
		return;
	}
	is_busy = 1;
	
	for(i = 0; i < size; i++) {
		res = &ret[i];
		count = res->count;
		updated = res->updated;

		if(updated) {
			has_data = 1;
			index = 0;
			memset(val_buf, 0x00, 16);
			for(j = 0; j < count; j++) {
				n = sprintf(val_buf + index, "%d,", res->val[j]);
				index += n;
			}
			val_buf[index - 1] = 0x00;
			//printk("val = %s\r\n", val_buf);
			cJSON_AddStringToObject(root, vehicle_data[i].name, val_buf);
			//printk("update [%s].\r\n", vehicle_data[i].name);
			result[i].updated = 0;
		}
	}

	if(has_data) {
		cJSON_AddStringToObject(root, "deviceid", (const char *)device_id);
		cJSON_AddNumberToObject(root, "msg_type", MSG_TYPE_UPLOAD);
		out = cJSON_Print(root);
		length = strlen(out);
	    
		sim900_write((uint8_t *)out, length);
		printf("%s\r\n", out);
	}
    cJSON_Delete(root);
    myfree(out);	

	is_busy = 0;
}

void handleData(uint8_t data)
{
    OSSemPost(rx_semaphore);
}

void SIM900_USART_IRQHandler(void)
{
    uint8_t data = 0;

    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL();
    OSIntNesting ++;
    OS_EXIT_CRITICAL();

    if(USART_GetITStatus(SIM900_USART, USART_IT_RXNE)
            != RESET) {
        data = USART_ReceiveData(SIM900_USART);
        rb_put(&rx_rb, &data, 1);
        handleData(data);
    }

    if(USART_GetITStatus(SIM900_USART, USART_IT_TXE)
            != RESET) {
        USART_ITConfig(SIM900_USART, USART_IT_TXE, DISABLE);
    }	
    OSIntExit();
}
